// wglprogram.cpp
//

#include "config.h"
#include "wgl/wglprogram.hpp"
#include "wgl/wgl_gl.hpp"
#include "wgl/wglshader.hpp"

#include "glm/gtc/type_ptr.hpp"

using namespace wgl;



// Program::attach_shader
//
void Program::attach_shader ( Shader &shader )
{
  shaders.push_back(&shader);
}



// Program::link
//
bool Program::link ()
{
  if (id == 0)
    id = gl.CreateProgram();

  for (auto s: shaders)
    {
      if (s->compile())
        {
          gl.AttachShader(id, s->get_id());
        }
      else
        {
          WGL_ERROR("shader compilation failed");
        }
    }

  WGL_TRACE("linking shader program");
  gl.LinkProgram(id);
  GLint status;
  gl.GetProgramiv(id, GL_LINK_STATUS, &status);
  if (status)
    {
      WGL_TRACE("linking ok");
    }
  else
    {
      WGL_ERROR("linking failed");
      GLint log_len, real_len;
      gl.GetProgramiv(id, GL_INFO_LOG_LENGTH, &log_len);
      GLchar log[log_len];
      gl.GetProgramInfoLog(id, log_len, &real_len, log);
      WGL_ERROR("compilation logs:\n%s", log);
      return false;
    }

  // debug
  GLint active_uniforms;
  gl.GetProgramiv(id, GL_ACTIVE_UNIFORMS, &active_uniforms);
  WGL_TRACE("active uniforms: %d", active_uniforms);

  GLint uniform_max_len;
  gl.GetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_len);
  WGL_TRACE("uniform max len: %d", uniform_max_len);
  
  return true;
}



// Program::get_uniform_location
//
GLint Program::get_uniform_location ( const char *name )
{
  GLint loc;

  if ((loc  = gl.GetUniformLocation(id, name)) < 0)
    {
      WGL_WARNING("[TODO] uniform location not found: %s", name);
    }

  return loc;
}



// Program::use
//
void Program::use ()
{
  gl.UseProgram(id);
}



// init
//
void UniformSlot::init ( Program &program,
                         const char *name )
{
  location = program.get_uniform_location(name);
}



// UniformSlot::setv3
//
void UniformSlot::setv3 ( const glm::vec3 &value )
{
  if (location >= 0)
    gl.Uniform3fv(location, 1, glm::value_ptr(value));
}



// UniformSlot::setv4
//
void UniformSlot::setv4 ( const glm::vec4 &value )
{
  if (location >= 0)
    gl.Uniform4fv(location, 1, glm::value_ptr(value));
}



// UniformSlot::setm3
//
void UniformSlot::setm3 ( const glm::mat3 &m )
{
  if (location >= 0)
    gl.UniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(m));
}



// UniformSlot::setm4
//
void UniformSlot::setm4 ( const glm::mat4 &value )
{
  if (location >= 0)
    gl.UniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
}
